package redis.clients.jedis;

import java.util.Objects;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocketFactory;

public final class DefaultJedisClientConfig implements JedisClientConfig {

    /*
     * 连接超时时间
     * <p>
     * 当客户端尝试连接到 Redis 服务器时，
     * 如果超过了指定的超时时间（默认为 2000 毫秒，即 2 秒），则连接将被中断
     */
    private final int connectionTimeoutMillis;


    /*
     * 连接建立后，客户端与服务器之间的数据传输超时时间
     *
     * 当客户端向服务器发送数据或从服务器接收数据时，
     * 如果超过了指定的超时时间（默认为 2000 毫秒，即 2 秒），则连接将被中断
     */
    private final int socketTimeoutMillis;


    /*
     * 阻塞式 socket 连接的超时时间
     *
     * 当客户端尝试与服务器建立阻塞式 socket 连接时，
     * 如果超过了指定的超时时间（默认为 2000 毫秒，即 2 秒），则连接将被中断。
     *
     * DefaultJedisClientConfig 的 blockingSocketTimeoutMillis 和 socketTimeoutMillis
     * 都是 Jedis 连接池中的重要配置项，但它们的作用有所不同。
     *
     * socketTimeoutMillis 是整个连接生命周期内的操作超时时间，包括了连接建立和数据读写等操作。
     * 一旦客户端与服务器之间的数据传输超过了这个设定的时间（默认为 2000 毫秒，即 2 秒），就会触发连接中断。
     *
     * blockingSocketTimeoutMillis 则是专门针对阻塞式 socket 连接设立的超时机制。
     * 当客户端尝试与服务器建立阻塞式 socket 连接时，
     * 如果超过这个设定的时间（默认为 2000 毫秒，即 2 秒）仍未成功建立起连接，
     * 那么这次连接尝试就会被中断。因此，这两个配置项虽然都是用来控制超时的，但其应用范围和作用场景是不同的。
     */
    private final int blockingSocketTimeoutMillis;


    /*
     * 用户名
     */
    private final String user;


    /*
     * 私有的、可变的字符串类型的成员变量 password
     *
     * volatile修饰:
     * 可能在多线程环境下被不同的线程访问和修改。通过添加volatile关键字，可以确保每次读取password变量时，
     * 都是直接从变量的内存地址中读取，而不是使用缓存的值，从而避免了因多线程导致的数据不一致问题。
     */
    private volatile String password;


    /*
     * 数据库编号
     */
    private final int database;


    /*
     * 指定客户端的名称
     */
    private final String clientName;

    private final boolean ssl;
    private final SSLSocketFactory sslSocketFactory;
    private final SSLParameters sslParameters;
    private final HostnameVerifier hostnameVerifier;

    private final HostAndPortMapper hostAndPortMapper;

    private DefaultJedisClientConfig(int connectionTimeoutMillis, int soTimeoutMillis,
                                     int blockingSocketTimeoutMillis, String user, String password, int database, String clientName,
                                     boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters,
                                     HostnameVerifier hostnameVerifier, HostAndPortMapper hostAndPortMapper) {
        this.connectionTimeoutMillis = connectionTimeoutMillis;
        this.socketTimeoutMillis = soTimeoutMillis;
        this.blockingSocketTimeoutMillis = blockingSocketTimeoutMillis;
        this.user = user;
        this.password = password;
        this.database = database;
        this.clientName = clientName;
        this.ssl = ssl;
        this.sslSocketFactory = sslSocketFactory;
        this.sslParameters = sslParameters;
        this.hostnameVerifier = hostnameVerifier;
        this.hostAndPortMapper = hostAndPortMapper;
    }

    @Override
    public int getConnectionTimeoutMillis() {
        return connectionTimeoutMillis;
    }

    @Override
    public int getSocketTimeoutMillis() {
        return socketTimeoutMillis;
    }

    @Override
    public int getBlockingSocketTimeoutMillis() {
        return blockingSocketTimeoutMillis;
    }

    @Override
    public String getUser() {
        return user;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public synchronized void updatePassword(String password) {
        if (!Objects.equals(this.password, password)) {
            this.password = password;
        }
    }

    @Override
    public int getDatabase() {
        return database;
    }

    @Override
    public String getClientName() {
        return clientName;
    }

    @Override
    public boolean isSsl() {
        return ssl;
    }

    @Override
    public SSLSocketFactory getSslSocketFactory() {
        return sslSocketFactory;
    }

    @Override
    public SSLParameters getSslParameters() {
        return sslParameters;
    }

    @Override
    public HostnameVerifier getHostnameVerifier() {
        return hostnameVerifier;
    }

    @Override
    public HostAndPortMapper getHostAndPortMapper() {
        return hostAndPortMapper;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {

        private int connectionTimeoutMillis = Protocol.DEFAULT_TIMEOUT;
        private int socketTimeoutMillis = Protocol.DEFAULT_TIMEOUT;
        private int blockingSocketTimeoutMillis = 0;

        private String user = null;
        private String password = null;
        private int database = Protocol.DEFAULT_DATABASE;
        private String clientName = null;

        private boolean ssl = false;
        private SSLSocketFactory sslSocketFactory = null;
        private SSLParameters sslParameters = null;
        private HostnameVerifier hostnameVerifier = null;

        private HostAndPortMapper hostAndPortMapper = null;

        private Builder() {
        }

        public DefaultJedisClientConfig build() {
            return new DefaultJedisClientConfig(connectionTimeoutMillis, socketTimeoutMillis,
                    blockingSocketTimeoutMillis, user, password, database, clientName, ssl, sslSocketFactory,
                    sslParameters, hostnameVerifier, hostAndPortMapper);
        }

        public Builder connectionTimeoutMillis(int connectionTimeoutMillis) {
            this.connectionTimeoutMillis = connectionTimeoutMillis;
            return this;
        }

        public Builder socketTimeoutMillis(int socketTimeoutMillis) {
            this.socketTimeoutMillis = socketTimeoutMillis;
            return this;
        }

        public Builder blockingSocketTimeoutMillis(int blockingSocketTimeoutMillis) {
            this.blockingSocketTimeoutMillis = blockingSocketTimeoutMillis;
            return this;
        }

        public Builder user(String user) {
            this.user = user;
            return this;
        }

        public Builder password(String password) {
            this.password = password;
            return this;
        }

        public Builder database(int database) {
            this.database = database;
            return this;
        }

        public Builder clientName(String clientName) {
            this.clientName = clientName;
            return this;
        }

        public Builder ssl(boolean ssl) {
            this.ssl = ssl;
            return this;
        }

        public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory) {
            this.sslSocketFactory = sslSocketFactory;
            return this;
        }

        public Builder sslParameters(SSLParameters sslParameters) {
            this.sslParameters = sslParameters;
            return this;
        }

        public Builder hostnameVerifier(HostnameVerifier hostnameVerifier) {
            this.hostnameVerifier = hostnameVerifier;
            return this;
        }

        public Builder hostAndPortMapper(HostAndPortMapper hostAndPortMapper) {
            this.hostAndPortMapper = hostAndPortMapper;
            return this;
        }
    }

    public static DefaultJedisClientConfig copyConfig(JedisClientConfig copy) {
        return new DefaultJedisClientConfig(copy.getConnectionTimeoutMillis(),
                copy.getSocketTimeoutMillis(), copy.getBlockingSocketTimeoutMillis(), copy.getUser(),
                copy.getPassword(), copy.getDatabase(), copy.getClientName(), copy.isSsl(),
                copy.getSslSocketFactory(), copy.getSslParameters(), copy.getHostnameVerifier(),
                copy.getHostAndPortMapper());
    }
}
